/*

Copyright (C) 2008 Victor Manuel Sanchez Cartagena, Juan Antonio Perez Ortiz


This file is part of ANTLRArbol.

ANTLRArbol is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

ANTLRArbol is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. 

*/

/** ANTLR v3 grammar */
grammar ANTLR;


tokens {
  DOC_COMMENT;
  PARSER; 
    LEXER;
    RULE;
    BLOCK;
    OPTIONAL;
    CLOSURE;
    POSITIVE_CLOSURE;
    SYNPRED;
    RANGE;
    CHAR_RANGE;
    EPSILON;
    ALT;
    EOR;
    EOB;
    EOA; // end of alt
    ID;
    ARG;
    ARGLIST;
    RET;
    LEXER_GRAMMAR;
    PARSER_GRAMMAR;
    TREE_GRAMMAR;
    COMBINED_GRAMMAR;
    INITACTION;
    LABEL; // $x used in rewrite rules
    TEMPLATE;
    SCOPE='scope';
    SEMPRED;
    GATED_SEMPRED; // {p}? =>
    SYN_SEMPRED; // (...) =>   it's a manually-specified synpred converted to sempred
    BACKTRACK_SEMPRED; // auto backtracking mode syn pred converted to sempred
    FRAGMENT='fragment';
    TREE_BEGIN='^(';
    ROOT='^';
    BANG='!';
    RANGE='..';
    REWRITE='->';
}


@members {
            

    /* Members of the parser class */
    
    
  public String traduccion;

  
  private ArrayList<String> atrSintetizados= new ArrayList<String>();
  private ArrayList<String> atrHeredados= new ArrayList<String>();
  private String nombreReglaActual="";

  private final static int TERMINAL_LITERAL=0;
  private final static int TERMINAL_TOKEN=1;
  private final static int TERMINAL_PUNTO=2;
  
  private final static int ATOM_TERMINAL=0;
  private final static int ATOM_REGLA=1;
  private final static int ATOM_OTRO=2;               
        
    
  private final static String STR_MEMBERS_1="private class AtributosVariablesANTLRArbol\n{\nprivate Hashtable<String,AttributesANTLRArbol> tablaAtributos;\npublic AtributosVariablesANTLRArbol()\n{\ntablaAtributos = new Hashtable<String,AttributesANTLRArbol>();\nAttributesANTLRArbol atributos;";  
  private final static String STR_MEMBERS_2="}\n public AttributesANTLRArbol getAtributos(String var){\n return tablaAtributos.get(var);\n}\n}\n private AtributosVariablesANTLRArbol atributosVariablesANTLRArbol= new AtributosVariablesANTLRArbol(); private AttributeTreeANTLRArbol arbANTLRArbol = new AttributeTreeANTLRArbol(";
  private final static String STR_MEMBERS_3=");\npublic String getTreeANTLRArbol(){ return arbANTLRArbol.generateOutput();}\nprivate Set<String> variablesIzqANTLRArbol;\nprivate Set<String> variablesDerANTLRArbol;\nprivate String nombreNodoANTLRArbol;";
  private final static String STR_INIT="DependenciesGraphANTLRArbol dependenciesGraphANTLRArbol = new DependenciesGraphANTLRArbol();\nAttributesANTLRArbol atrANTLRArbol;\n";
  private final static String STR_IMPORT_HASH="import org.ANTLRArbol.lib.* ;\n import java.util.Hashtable;\nimport java.util.HashSet;\nimport java.util.Set;";
  
  
  private static int numeroEtiqueta=0;
  
  private String argumentosArbolDependencias="0,true,true";
  
  private String nuevaEtiquetaANTLRArbol()
  {
    String retorno="etiquetaANTLRArbol"+numeroEtiqueta;
    numeroEtiqueta++;
    return retorno;
  }
  
  public void setArgumentosArbolDependencias(String args)
  {
     argumentosArbolDependencias=args;
  }
  
  private int ambitoANTLRArbol;

}

@parser::header{
package org.ANTLRArbol.grammars.ANTLR;
import org.ANTLRArbol.lib.AuxiliaryCodeANTLRArbol;
import java.util.Set;

}

@lexer::header{
package org.ANTLRArbol.grammars.ANTLR;

}



grammarDef
  @init{
      traduccion="";
      boolean definidoHeader=false;
      boolean definidoMembers = false;
      String postTraduccion="";
      String definicionAtributos="";
  }
  :
  (DOC_COMMENT {traduccion=traduccion+$DOC_COMMENT.text+" ";} )?
      ( 'lexer'  {traduccion+="lexer ";}    // pure lexer
      |   'parser' {traduccion+="parser ";}   // pure parser
      |   'tree'   {traduccion+="tree ";}     // a tree parser
  |
  )
      'grammar' id ';' {traduccion=traduccion+"grammar "+$id.text+" ; ";} 
      (optionsSpec {traduccion=traduccion+$optionsSpec.text+" \n";} )? 
      (tokensSpec {traduccion=traduccion+$tokensSpec.text+" \n";} )? 
      (attrScope {traduccion=traduccion+$attrScope.text+" \n";} )* 
      (action 
      {
        if(definidoMembers)
        {
           postTraduccion=postTraduccion+$action.s+" \n";
           if($action.rheader)
              definidoHeader=true;
        }
        else
        {
           traduccion=traduccion+$action.s+" \n";
           if($action.rmembers)
           {
              definidoMembers=true;
              postTraduccion=postTraduccion+$action.postS+" \n";
           }
           else     
           if($action.rheader)
              definidoHeader=true;
        }
      } 
      )*
      {
        if(!definidoHeader)
        {
          if(definidoMembers)
             postTraduccion=postTraduccion+"\n@header{\n"+ STR_IMPORT_HASH +"\n}\n";
          else
             traduccion=traduccion+"\n@header{\n"+ STR_IMPORT_HASH +"\n}\n";
        }
          
        if(!definidoMembers)
        {
          traduccion=traduccion+"\n@members{\n"+ STR_MEMBERS_1;
          postTraduccion= postTraduccion+STR_MEMBERS_2+argumentosArbolDependencias+STR_MEMBERS_3+"}\n";
        }
        
      }
      (rule {postTraduccion=postTraduccion+$rule.s+" \n"; definicionAtributos+=$rule.defAtributos;} )+
      //traduccion: everything before AtributosVariablesANTLRArbol constructor
      //definicionAtributos: constructor's content.
      //postTraduccion: everything after constructor.
      { traduccion = traduccion + definicionAtributos+ postTraduccion;}
      EOF     
    ;

tokensSpec 
  :
  TOKENS  tokenSpec+ '}'
  ;

tokenSpec
  : TOKEN_REF ( '='  (lit=STRING_LITERAL|lit=CHAR_LITERAL) | )  ';'
  ;

attrScope
  : 'scope' id ACTION
  ;

/** Match stuff like @parser::members {int i;} */
action returns [String s, boolean rheader, boolean rmembers, String postS]
@init
{
  $s="";
}
  : 
  '@' (actionScopeName '::' {$s=$actionScopeName.text+"::";} )? id ACTION
    {
      $rmembers=false;
      String textoaction = $ACTION.text;
      if($s.equals("parser::") || $s.isEmpty())
      {
        if($id.text.equals("members"))
        {
          $postS = STR_MEMBERS_2+argumentosArbolDependencias+STR_MEMBERS_3 + "}\n";
          textoaction = textoaction.substring(0,textoaction.length()-1) + STR_MEMBERS_1;
          $rmembers=true;
        }
      
        
        if($id.text.equals("header"))
        {
          textoaction = textoaction.substring(0,textoaction.length()-1) + STR_IMPORT_HASH + "}\n";
          $rheader=true;
        }
      
      }
      $s = "@"+$s+$id.text+textoaction;
    }
  ;

/** Sometimes the scope names will collide with keywords; allow them as
 *  ids for action scopes.
 */
actionScopeName
  : id
  | l='lexer' 
    |   p='parser'  
  ;

optionsSpec
  : OPTIONS (option ';')+ '}'
  ;

option 
    :   id '=' optionValue
  ;
  
optionValue 
    :   id
    |   STRING_LITERAL
    |   CHAR_LITERAL
    |   INT
    | s='*' 
    ;

/* 
  In a rule, we store the names of its synthesized and inherited attributes.
  In each alternative of the rule, we notify AttributeTreeANTLRArbol the value of these attributes.
  Inherited attributes value is notified at the beginning of an alternative, and synthesized attributes value at the end.  
  
 */
rule returns [String s, String defAtributos]
@init
{
  boolean definidoReturns = false;
  boolean definidoInit = false;
  $defAtributos="";
}
  : {$s="\n\n";}
    (DOC_COMMENT {$s=$s+" "+$DOC_COMMENT.text;})?
    ( modifier=('protected'|'public'|'private'|'fragment') {$s=$s+" "+$modifier.text;} )?
    id {$s=$s+" "+$id.text; nombreReglaActual=$id.text; }
    ('!' {$s=$s+" !";})?
    ( arg=ARG_ACTION 
    {
    
      if(!$arg.text.isEmpty())
      {
        String cadArg = $arg.text;
        $s=$s+" ["+cadArg+"]";
        
        //Store names of inherited attributes
        
        //Separate attributes by commas
        String[] strAtributos = cadArg.split(",");
        for(int i=0; i< strAtributos.length;i++)
        {
          //Remove spaces at the beginning and at the end
          String atributo = strAtributos[i].trim();
          
          //Separate name and type of attribute.
          String[] defAtributo = atributo.split(" ");
          
          //Add name to the list
          atrHeredados.add(defAtributo[defAtributo.length -1]);
        }
      }
    
    } 
    )?
    
    ( 'returns' rt=ARG_ACTION 
    {
      definidoReturns = true;
      $s=$s+" returns ";
      if(!$rt.text.isEmpty())
      {
        $s=$s+"["+$rt.text+", String ANTLRArbolNombreNodo ] ";
        
        //Store names of synthesized attributes
        
        //Separate attributes by commas
        String[] strAtributos = $rt.text.split(",");
        for(int i=0; i< strAtributos.length;i++)
        {
           //Remove spaces at the beginning and at the end      
          String atributo = strAtributos[i].trim();
          
        //Separate name and type of attribute.
          String[] defAtributo = atributo.split(" ");
          
          //Add name to the list
          atrSintetizados.add(defAtributo[defAtributo.length -1]);
        }
        
      }
      else
      {
         $s=$s+"[String ANTLRArbolNombreNodo ] ";
      }
    }
     )?
     
     {
        if(!definidoReturns)
        {
            //parser rule
            if($id.text.substring(0,1).toLowerCase().equals($id.text.substring(0,1)))
                $s = $s+" returns [String ANTLRArbolNombreNodo] ";
        }
     }
     
    (throwsSpec {$s=$s+" "+$throwsSpec.text;})? (optionsSpec {$s=$s+" "+$optionsSpec.text;})? (ruleScopeSpec {$s=$s+" "+$ruleScopeSpec.text;})? 
    (
      resultRuleAction=ruleAction 
      {
        
        //It is a parser rule
        if($id.text.substring(0,1).toLowerCase().equals($id.text.substring(0,1)))
        {
             $s=$s+" "+$resultRuleAction.s;
            if($resultRuleAction.esInit)
            {
                definidoInit=true;
            }
        }
        else
        {         
            $s=$s+" "+$resultRuleAction.text;
        }
       
      }
    )*
    
    {
        if(!definidoInit && $id.text.substring(0,1).toLowerCase().equals($id.text.substring(0,1)))
        {
            $s = $s + "\n@init{\n"+STR_INIT+"}\n";
        }
    }
    
    ':'    altList[$id.text]  ';' 
    {
    
      $defAtributos="atributos = new AttributesANTLRArbol();\n";
      
      for(int i=0; i< atrHeredados.size();i++)
      {
          $defAtributos=$defAtributos+"atributos.heredados.add(\""+ atrHeredados.get(i) +"\");\n";
      }
      
       for(int i=0; i< atrSintetizados.size();i++)
      {
          $defAtributos=$defAtributos+"atributos.sintetizados.add(\""+ atrSintetizados.get(i) +"\");\n";
      }
      
      $defAtributos=$defAtributos+"tablaAtributos.put(\""+$id.text+"\",atributos);\n";
      
    
     //Empty list of synthesized attributes and list of inherited attributes.
     atrHeredados.clear();
     atrSintetizados.clear();
     nombreReglaActual="";
     
     /*
      If rule's id starts with a lower case character (it's a parser rule), translation
      is altList's translation.
      Else (the rule is a lexer rule), translation is simply the text of altList
      
     */
     
     if($id.text.substring(0,1).toLowerCase().equals($id.text.substring(0,1)))
        $s=$s+"\n:" +$altList.s+" ;\n";
     else
        $s=$s+"\n:" +$altList.text+" ;\n";
     
    }
    (exceptionGroup {$s=$s+" "+$exceptionGroup.text;} )?
  ;

/** Match stuff like @init {int i;} 
    If the action is "@init", add some declarations.
*/
ruleAction returns [String s, boolean esInit]
  : '@' resId=id resAct=ACTION 
  {
     String txtAction =AuxiliaryCodeANTLRArbol.processBlock($resAct.text,0);
     if($id.text.equals("init"))
     {
         $esInit=true;
         $s = "\n@"+$resId.text+"{"+STR_INIT+txtAction.substring(1,txtAction.length());
     }
     else
     {
         $esInit=false;
         $s = "\n@"+$resId.text+$resAct.text+"\n";
     }
  }
  ;

throwsSpec
  : 'throws' id ( ',' id )* 
  ;

ruleScopeSpec 
  : 'scope' ACTION 
  | 'scope' id (',' id)* ';' 
  | 'scope' ACTION
    'scope' id (',' id)* ';'
    
  ;

block returns [String s]
    :   lp='(' {$s=" ( ";ambitoANTLRArbol++;}
    ( (opts=optionsSpec {$s=$s+$opts.text;} )? ':' {$s=$s+":";} )?
    a1=alternative r1=rewrite {$s=$s+" "+$a1.s+" "+$r1.text;} ( '|' a2=alternative r2=rewrite {$s=$s+" | "+$a2.s+" "+$r2.text;} )*
        rp=')' {ambitoANTLRArbol--; $s=$s+" {dependenciesGraphANTLRArbol.scopeExit("+ambitoANTLRArbol+");} ) ";}
    ;
/*
Add code to draw the tree at the beginning and in the end of each alternative.
*/
altList [String rp] returns [String s]
    :  {ambitoANTLRArbol=0;} a1=alternative r1=rewrite 
        { 
           $s=" { \$ANTLRArbolNombreNodo=arbANTLRArbol.startNotTerminal(\""+ $rp +"\"); ";
           for(int i=0; i<atrHeredados.size();i++)
           {
              $s=$s+"\narbANTLRArbol.showInherited(\""+atrHeredados.get(i)+"\",$"+atrHeredados.get(i)+");";
              $s=$s+"\ndependenciesGraphANTLRArbol.addVariable(\"\\\$"+atrHeredados.get(i)+"\",0);";
           }
           for(int i=0; i<atrSintetizados.size();i++)
           {
             $s=$s+"\ndependenciesGraphANTLRArbol.addVariable(\"\\\$"+atrSintetizados.get(i)+"\",0);";
           }
          $s=$s+ "} "+$a1.s+" "+$r1.text+"{ ";
            for(int i=0; i<atrSintetizados.size();i++)
             {
              $s=$s+"\narbANTLRArbol.showSynthesized(\""+atrSintetizados.get(i)+"\",$"+atrSintetizados.get(i)+");";
              $s=$s+"\nAuxiliaryCodeANTLRArbol.detectDependencySynthesized(\"\\\$"+atrSintetizados.get(i)+"\",dependenciesGraphANTLRArbol,arbANTLRArbol);";
             }
            $s=$s+"\n arbANTLRArbol.endNotTerminal();";
           $s=$s+ " }\n";          
           
        } 
        ( '|' {ambitoANTLRArbol=0;}  a2=alternative r2=rewrite 
           {
               $s=$s + " | { \$ANTLRArbolNombreNodo=arbANTLRArbol.startNotTerminal(\""+ $rp +"\"); ";
               for(int i=0; i<atrHeredados.size();i++)
               {
                  $s=$s+"\narbANTLRArbol.showInherited(\""+atrHeredados.get(i)+"\",$"+atrHeredados.get(i)+");";
                  $s=$s+"\ndependenciesGraphANTLRArbol.addVariable(\"\\\$"+atrHeredados.get(i)+"\",0);";
               }
               for(int i=0; i<atrSintetizados.size();i++)
               {
                 $s=$s+"\ndependenciesGraphANTLRArbol.addVariable(\"\\\$"+atrSintetizados.get(i)+"\",0);";
               }
                $s=$s+ "} "+$a2.s+" "+$r2.text+"{ ";
                for(int i=0; i<atrSintetizados.size();i++)
               {
                  $s=$s+"\narbANTLRArbol.showSynthesized(\""+atrSintetizados.get(i)+"\",$"+atrSintetizados.get(i)+");";
                  $s=$s+"\nAuxiliaryCodeANTLRArbol.detectDependencySynthesized(\"\\\$"+atrSintetizados.get(i)+"\",dependenciesGraphANTLRArbol,arbANTLRArbol);";
               }               
              $s=$s+"\n arbANTLRArbol.endNotTerminal();";
               $s=$s+ " }\n";
              
            } 
        )*
    
    ;

alternative returns [String s]
@init
{
  $s="";
}
    :   (e=element {$s=$s+$e.s;} )+
    |  
    ;

exceptionGroup 
  : ( exceptionHandler )+ ( finallyClause )?
  | finallyClause
    ;

exceptionHandler 
    :    'catch' ARG_ACTION ACTION 
    ;

finallyClause 
    :    'finally' ACTION 
    ;

element returns [String s]
  : e=elementNoOptionSpec {$s = $e.s+" ";} 
  ;

elementNoOptionSpec returns [String s]
  : id (labelOp='='|labelOp='+=') atom 
    {
       $s  = $atom.accionSemanticaPrevia+ $id.text+$labelOp.text+$atom.s;
       if($atom.tipoAtom==ATOM_REGLA)
       {
         $s = $s + " {  dependenciesGraphANTLRArbol.addVariableANTLR(\"\\\$"+$id.text+"\",\"\\\$"+$atom.nombreRegla+"\",\$"+$id.text+".ANTLRArbolNombreNodo,atributosVariablesANTLRArbol.getAtributos(\""+$atom.nombreRegla+"\"),"+ambitoANTLRArbol+"); }";
       }
       else
       if($atom.tipoAtom==ATOM_TERMINAL)
       {
          if($atom.tipoTerminal==TERMINAL_TOKEN)
          {
            $s=$s + " { nombreNodoANTLRArbol = arbANTLRArbol.terminal(\""+ $atom.nombreRegla +"\",$"+$id.text+".text); dependenciesGraphANTLRArbol.addVariableANTLR(\"\\\$"+$id.text+"\",\"\\\$"+$atom.nombreRegla+"\",nombreNodoANTLRArbol,null,"+ambitoANTLRArbol+"); } ";
          }
          else if($atom.tipoTerminal==TERMINAL_LITERAL)
          {
            $s=$s + " { nombreNodoANTLRArbol = arbANTLRArbol.terminal(\""+ $atom.nombreRegla +"\",\""+$atom.nombreRegla+"\"); dependenciesGraphANTLRArbol.addVariableANTLR(\"\\\$"+$id.text+"\",\"\",nombreNodoANTLRArbol,null,"+ambitoANTLRArbol+"); } ";
          }
          else
          {
            $s=$s + " { arbANTLRArbol.terminal(\""+ $atom.nombreRegla +"\",\""+$atom.nombreRegla+"\"); } ";
          }
       }
    }
    ( ebnfSuffix  {$s=" ( "+$s+" ) "+$ebnfSuffix.text;}
    |       
    )
  | id (labelOp='='|labelOp='+=') block {$s  = $id.text+$labelOp.text+$block.s;}
    ( ebnfSuffix  {$s=$s+$ebnfSuffix.text;}
    |       
    )
  | atom 
    {
      String var = nuevaEtiquetaANTLRArbol();
       $s = $atom.accionSemanticaPrevia +var+"="+$atom.s;
       if($atom.tipoAtom==ATOM_REGLA)
       {
         $s = $s + " {dependenciesGraphANTLRArbol.addVariableANTLR(\"\\\$"+$atom.nombreRegla+"\",\"\",\$"+var+".ANTLRArbolNombreNodo,atributosVariablesANTLRArbol.getAtributos(\""+$atom.nombreRegla+"\"),"+ambitoANTLRArbol+"); } ";
       }
       else
       if($atom.tipoAtom==ATOM_TERMINAL)
       {
          if($atom.tipoTerminal==TERMINAL_TOKEN)
          {
            $s=$s + " { nombreNodoANTLRArbol = arbANTLRArbol.terminal(\""+ $atom.nombreRegla +"\",$"+var+".text);  dependenciesGraphANTLRArbol.addVariableANTLR(\"\\\$"+$atom.nombreRegla+"\",\"\",nombreNodoANTLRArbol,null,"+ambitoANTLRArbol+"); } ";
          }
          else if($atom.tipoTerminal==TERMINAL_LITERAL)
          {
            $s=$s + " { arbANTLRArbol.terminal(\""+ AuxiliaryCodeANTLRArbol.escape($atom.nombreRegla) +"\",\""+AuxiliaryCodeANTLRArbol.escape($atom.nombreRegla)+"\"); } ";
          }
          else
          {
            $s=$s + " { arbANTLRArbol.terminal(\""+ $atom.nombreRegla +"\",\""+$atom.nombreRegla+"\"); } ";
          }
       }
    }
    ( ebnfSuffix  {$s=" ( "+$s+" ) "+$ebnfSuffix.text;}
    |       
    )
  | ebnf {$s=$ebnf.s;}
  |   ACTION {$s=AuxiliaryCodeANTLRArbol.processBlock($ACTION.text,ambitoANTLRArbol); }
  |   SEMPRED {$s=$SEMPRED.text;} ( '=>' {$s=$s+"=>";}  |  )
  |   treeSpec {$s=$treeSpec.text;}
  ;

atom returns [String s, int tipoAtom, int tipoTerminal,String nombreRegla, String accionSemanticaPrevia]
@init
{
  $nombreRegla="";
  $accionSemanticaPrevia="";
}
:   
    range {$s=$range.text; $tipoAtom=ATOM_OTRO;} ( (op='^'|op='!') {$s=$s+$op.text;}  |   )
    |   terminal 
      {
        $tipoAtom=ATOM_TERMINAL;
        $tipoTerminal = $terminal.tipo;
        $nombreRegla = $terminal.text;
        $s = " " + $terminal.text;
        
      }
    | notSet {$s=$notSet.text; $tipoAtom=ATOM_OTRO;} ( (op='^'|op='!') {$s=$s+$op.text;} |   )
    |   RULE_REF  {$s=$RULE_REF.text;} 
        ( arg=ARG_ACTION        
        {
          $accionSemanticaPrevia = $accionSemanticaPrevia + " { \n";
         ArrayList<Set<String>> variablesExpresiones = AuxiliaryCodeANTLRArbol.processExpressionList($arg.text);
          for(int i=0; i<variablesExpresiones.size(); i++)
          {
            for(String variable: variablesExpresiones.get(i))
            {
              if(variable.startsWith("$"))
                $accionSemanticaPrevia = $accionSemanticaPrevia + "AuxiliaryCodeANTLRArbol.detectDependencyInherited(\"\\"+variable+"\","+i+", arbANTLRArbol, atributosVariablesANTLRArbol.getAtributos(\""+$RULE_REF.text+"\").heredados, atributosVariablesANTLRArbol.getAtributos(\""+nombreReglaActual+"\").heredados , dependenciesGraphANTLRArbol,"+ambitoANTLRArbol+");\n";
              else
                $accionSemanticaPrevia = $accionSemanticaPrevia + "AuxiliaryCodeANTLRArbol.detectDependencyInherited(\""+variable+"\","+i+", arbANTLRArbol, atributosVariablesANTLRArbol.getAtributos(\""+$RULE_REF.text+"\").heredados, atributosVariablesANTLRArbol.getAtributos(\""+nombreReglaActual+"\").heredados , dependenciesGraphANTLRArbol,"+ambitoANTLRArbol+");\n";
            }
          }
         $accionSemanticaPrevia = $accionSemanticaPrevia + " } \n";
          $s= $s+"["+$arg.text+"] "; 
        }         
        )? 
        
        ( (op='^'|op='!') {$s=$s+$op.text;} )? {$nombreRegla = $RULE_REF.text; $tipoAtom=ATOM_REGLA;}
    ;

notSet
  : '~'
    ( notTerminal 
    | block   
    )
  ;

treeSpec
  : '^(' element ( element )+ ')' 
  ;

/** Matches ENBF blocks (and token sets via block rule) */
ebnf returns [String s]
  : block {$s=$block.s;}
    ( '?' {$s=$s+"?";}  
    | '*' {$s=$s+"*";}  
    | '+'   {$s=$s+"+";}
    |   '^'   {$s=$s+"^";}
    |   '!'   {$s=$s+"!";}
    |   '=>' {$s=$s+"=>";}
    |     
    )
  ;

range 
  : c1=CHAR_LITERAL RANGE c2=CHAR_LITERAL
  ;

terminal returns [int tipo]
    :   ( CHAR_LITERAL  {$tipo=TERMINAL_LITERAL;}     
        
    |   TOKEN_REF {$tipo=TERMINAL_TOKEN;}
      ( ARG_ACTION        
      |             
      )
    |   STRING_LITERAL {$tipo=TERMINAL_LITERAL;}
    |   '.' {$tipo=TERMINAL_PUNTO;}           
    ) 
    ( '^'             
    | '!'             
    )?
  ;

notTerminal
  :   CHAR_LITERAL
  | TOKEN_REF
  | STRING_LITERAL
  ;
  
ebnfSuffix 

  : '?' 
    | '*' 
    | '+'
  ;
  


// R E W R I T E  S Y N T A X

rewrite

  : ('->' SEMPRED rewrite_alternative)*
    '->' last=rewrite_alternative
  |
  ;

rewrite_alternative
options {backtrack=true;}
  :  rewrite_template 
  | rewrite_tree_alternative 
    |   /* empty rewrite */ 
  ;
  
rewrite_template_block
    :   lp='(' rewrite_template ')' 
    ;

rewrite_tree_block 
    :   lp='(' rewrite_tree_alternative ')'
      
    ;

rewrite_tree_alternative 
    : rewrite_tree_element+ 
    ;

rewrite_tree_element
  : rewrite_tree_atom
  | rewrite_tree_atom ebnfSuffix
    
  |   rewrite_tree
    ( ebnfSuffix
      
    | 
    )
  |   rewrite_tree_ebnf
  ;

rewrite_tree_atom 
    :   CHAR_LITERAL
  |   TOKEN_REF ARG_ACTION? 
    |   RULE_REF
  |   STRING_LITERAL
  |   d='$' id 
  | ACTION
  ;

rewrite_tree_ebnf 

  : rewrite_tree_block ebnfSuffix 
  ;
  
rewrite_tree 
  : '^(' rewrite_tree_atom rewrite_tree_element* ')'
    
  ;

/** Build a tree for a template rewrite:
      ^(TEMPLATE (ID|ACTION) ^(ARGLIST ^(ARG ID ACTION) ...) )
    where ARGLIST is always there even if no args exist.
    ID can be "template" keyword.  If first child is ACTION then it's
    an indirect template ref

    -> foo(a={...}, b={...})
    -> ({string-e})(a={...}, b={...})  // e evaluates to template name
    -> {%{$ID.text}} // create literal template from string (done in ActionTranslator)
  -> {st-expr} // st-expr evaluates to ST
 */
rewrite_template returns [int a]
  :   // -> template(a={...},...) "..."    inline template
    id lp='(' rewrite_template_args ')'
    st=( DOUBLE_QUOTE_STRING_LITERAL | DOUBLE_ANGLE_STRING_LITERAL )
    

  | // -> foo(a={...}, ...)
    rewrite_template_ref

  | // -> ({expr})(a={...}, ...)
    rewrite_indirect_template_head

  | // -> {...}
    ACTION
  ;

/** -> foo(a={...}, ...) */
rewrite_template_ref 
  : id lp='(' rewrite_template_args ')'
    
  ;

/** -> ({expr})(a={...}, ...) */
rewrite_indirect_template_head 
  : lp='(' ACTION ')' '(' rewrite_template_args ')'
    
  ;

rewrite_template_args 
  : rewrite_template_arg (',' rewrite_template_arg)*
    
  | 
  ;

rewrite_template_arg 
  :   id '=' ACTION
  ;

id returns [String s] 
: TOKEN_REF 
  | RULE_REF 
  ;

// L E X I C A L   R U L E S

SL_COMMENT
 	:	'//'
 	 	(	' $ANTLR ' SRC // src directive
 		|	~('\r'|'\n')*
		)
		'\r'? '\n'
		{$channel=HIDDEN;}
	;

ML_COMMENT
	:	'/*' {if (input.LA(1)=='*') $type=DOC_COMMENT; else $channel=HIDDEN;} .* '*/'
	;

CHAR_LITERAL
	:	'\'' LITERAL_CHAR '\''
	;

STRING_LITERAL
	:	'\'' LITERAL_CHAR LITERAL_CHAR* '\''
	;

fragment
LITERAL_CHAR
	:	ESC
	|	~('\''|'\\')
	;

DOUBLE_QUOTE_STRING_LITERAL
	:	'"' (ESC | ~('\\'|'"'))* '"'
	;

DOUBLE_ANGLE_STRING_LITERAL
	:	'<<' .* '>>'
	;

fragment
ESC	:	'\\'
		(	'n'
		|	'r'
		|	't'
		|	'b'
		|	'f'
		|	'"'
		|	'\''
		|	'\\'
		|	'>'
		|	'u' XDIGIT XDIGIT XDIGIT XDIGIT
		|	. // unknown, leave as it is
		)
	;

fragment
XDIGIT :
		'0' .. '9'
	|	'a' .. 'f'
	|	'A' .. 'F'
	;

INT	:	'0'..'9'+
	;

ARG_ACTION
	:	NESTED_ARG_ACTION
	;

fragment
NESTED_ARG_ACTION :
	'['
	(	options {greedy=false; k=1;}
	:	NESTED_ARG_ACTION
	|	ACTION_STRING_LITERAL
	|	ACTION_CHAR_LITERAL
	|	.
	)*
	']'
	{setText(getText().substring(1, getText().length()-1));}
	;

ACTION
	:	NESTED_ACTION ( '?' {$type = SEMPRED;} )?
	;

fragment
NESTED_ACTION :
	'{'
	(	options {greedy=false; k=2;}
	:	NESTED_ACTION
	|	SL_COMMENT
	|	ML_COMMENT
	|	ACTION_STRING_LITERAL
	|	ACTION_CHAR_LITERAL
	|	.
	)*
	'}'
   ;

fragment
ACTION_CHAR_LITERAL
	:	'\'' (ACTION_ESC|~('\\'|'\'')) '\''
	;

fragment
ACTION_STRING_LITERAL
	:	'"' (ACTION_ESC|~('\\'|'"'))* '"'
	;

fragment
ACTION_ESC
	:	'\\\''
	|	'\\' '"' // ANTLR doesn't like: '\\"'
	|	'\\' ~('\''|'"')
	;

TOKEN_REF
	:	'A'..'Z' ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
	;

RULE_REF
	:	'a'..'z' ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
	;

/** Match the start of an options section.  Don't allow normal
 *  action processing on the {...} as it's not a action.
 */
OPTIONS
	:	'options' WS_LOOP '{'
	;
	
TOKENS
	:	'tokens' WS_LOOP '{'
	;

/** Reset the file and line information; useful when the grammar
 *  has been generated so that errors are shown relative to the
 *  original file like the old C preprocessor used to do.
 */
fragment
SRC	:	'src' ' ' file=ACTION_STRING_LITERAL ' ' line=INT
	;

WS	:	(	' '
		|	'\t'
		|	'\r'? '\n'
		)+
		{$channel=HIDDEN;}
	;

fragment
WS_LOOP
	:	(	WS
		|	SL_COMMENT
		|	ML_COMMENT
		)*
	;


